#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use autodie;

use FindBin '$RealBin';
use lib "$RealBin/lib";

use Vnlog::Util qw(parse_options read_and_preparse_input ensure_all_legends_equivalent close_nondev_inputs reconstruct_substituted_command);



# This comes from the struct option long_options in tail.c in GNU coreutils
my @specs = ( # options with no args
             "silent|quiet|q",
             "zero-terminated|z",
             "verbose|v",
             "retry",
             "F",
             "f",

             # options with args
             "bytes|c=s",
             "lines|n=s",
             "sleep-interval|s=f",
             "max-unchanged-stats=i",
             "pid=i",

             # '--follow' and '--follow=...' are valid. '--follow ...' is NOT
             # valid. parse_options handles that
             "follow:s",

             "vnl-tool=s",
             "help");




my %options_unsupported = ( 'bytes' => <<'EOF',
vnlog is line-oriented, so I refuse to split lines
EOF
                            'zero-terminated' => <<'EOF'
vnlog is built on assuming a particular record separator
EOF
                          );

my ($filenames,$options) = parse_options(\@ARGV, \@specs, 0, <<EOF);
  $0 [options] logfile logfile logfile ... < logfile

The most common options are (mostly from the GNU tail manpage)

  -n, --lines=[+]NUM
         output the last NUM lines, instead of the last 10;
         or use -n +NUM to output starting with line NUM

  -f, --follow[={name|descriptor}]
         output appended data as the file grows;
         an absent option argument means 'descriptor'

  --vnl-tool tool
       Specifies the path to the tool we're wrapping. By default we wrap 'tail',
       so most people can omit this
EOF
$options->{'vnl-tool'} //= 'tail';

if( defined $options->{follow}         &&
    length($options->{follow}) > 0     &&
    $options->{follow} ne 'name'       &&
    $options->{follow} ne 'descriptor' )
{
    die "--follow can be given ONLY as --follow=name or --follow=descriptor";
}



for my $key(keys %$options)
{
    if($options_unsupported{$key})
    {
        my $keyname = length($key) == 1 ? "-$key" : "--$key";
        die("I don't support $keyname: $options_unsupported{$key}");
    }
}

my $inputs = read_and_preparse_input($filenames,
                                     # read all lines, including comments
                                     ['cat']);
ensure_all_legends_equivalent($inputs);
say '# ' . join(' ', @{$inputs->[0]{keys}});


# When looking at normal files, tail needs the original filenames to be able to
# do its thing. A pipe filter won't do; especially if we're doing a tail -f. But
# if the thing I'm tailing is a pipe to begin with, I need to give it that pipe.
# The NAME of the device won't do. Thus I call reconstruct_substituted_command()
# telling it to substitute the dev filenames, but leave non-dev filenames alone.
#
# Additionally, since I don't use the file filter anymore, I close those pipes

my $ARGV_new = reconstruct_substituted_command($inputs, $options, [], \@specs, 1);
close_nondev_inputs($inputs);
exec $options->{'vnl-tool'}, @$ARGV_new;



__END__

=head1 NAME

vnl-tail - tail a log file, preserving the legend

=head1 SYNOPSIS

 $ read_temperature | tee temp.vnl
 # temperature
 29.5
 30.4
 28.3
 22.1
 ... continually produces data

 ... at the same time, in another terminal
 $ vnl-tail -f temp.vnl
 # temperature
 28.3
 22.1
 ... outputs data as it comes in

=head1 DESCRIPTION

  Usage: vnl-tail [options] logfile logfile logfile ... < logfile

This tool runs C<tail> on given vnlog files in various ways. C<vnl-tail> is a
wrapper around the GNU coreutils C<tail> tool. Since this is a wrapper, most
commandline options and behaviors of the C<tail> tool are present; consult the
L<tail(1)> manpage for detail. The differences from GNU coreutils C<tail> are

=over

=item *

The input and output to this tool are vnlog files, complete with a legend

=item *

C<-c> is not supported because vnlog really doesn't want to break up lines

=item *

C<--zero-terminated> is not supported because vnlog assumes
newline-separated records

=item *

By default we call the C<tail> tool to do the actual work. If the underlying
tool has a different name or lives in an odd path, this can be specified by
passing C<--vnl-tool TOOL>

=back

Past that, everything C<tail> does is supported, so see that man page for
detailed documentation.

=head1 COMPATIBILITY

I use GNU/Linux-based systems exclusively, but everything has been tested
functional on FreeBSD and OSX in addition to Debian, Ubuntu and CentOS. I can
imagine there's something I missed when testing on non-Linux systems, so please
let me know if you find any issues.

=head1 SEE ALSO

L<tail(1)>

=head1 REPOSITORY

https://github.com/dkogan/vnlog/

=head1 AUTHOR

Dima Kogan C<< <dima@secretsauce.net> >>

=head1 LICENSE AND COPYRIGHT

Copyright 2018 Dima Kogan C<< <dima@secretsauce.net> >>

This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option) any
later version.

=cut
